/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2008 Intel Corporation. All Rights Reserved.
//
//
*/

#include "umc_defs.h"
#if defined(UMC_ENABLE_AVS_VIDEO_ENCODER)

#include <trace_log.h>
#include "umc_avs_enc_compressor.h"
#include "umc_avs_picture_header.h"
#include "umc_avs_slice.h"
#include "umc_avs_sequence_header.h"
#include "umc_avs_enc_tables.h"
#include "umc_avs_enc_vlc.h"

#include "umc_avs_enc_bit_stream.h"

namespace UMC
{

void AVSCompressor::CompressBMacroBlocksRow(void)
{
    Ipp32u MbTypeIndex;

    MbMax = m_encCtx.MbIndex + m_encCtx.MbWidth;

    // we can use MbIndex either from decoding and reconstructing contexts
    MbIndex = m_encCtx.MbIndex;

    PrepareNeighbours(&m_encCtx);
    PrepareDisassembling();
    PrepareDecoding();
    PrepareReconstruction();
    CompressSliceHeader();

    do
    {
        if (0 == MbIndex)
        {
            WRITE_TRACE_LOG(_enc_tst, "Frame num is  ", m_encCtx.m_pPicHeader->picture_distance - 1);
            WRITE_TRACE_LOG(_vec_tst, "Frame num is  ", m_encCtx.m_pPicHeader->picture_distance - 1);
            WRITE_TRACE_LOG(_vec_final_tst, "Frame num is  ", m_encCtx.m_pPicHeader->picture_distance - 1);
            WRITE_TRACE_LOG(_rec_tst_enc, "Frame num is  ", m_encCtx.m_pPicHeader->picture_distance - 1);
        }
        WRITE_TRACE_LOG_MB_TITLE(_dis_tst, MbIndex, m_disCtx.MbWidth, m_disCtx.MbHeight, 0 == m_disCtx.m_pPicHeader->progressive_frame);
        WRITE_TRACE_LOG_MB_TITLE(_enc_tst, MbIndex, m_encCtx.MbWidth, m_encCtx.MbHeight, 0 == m_encCtx.m_pPicHeader->progressive_frame);
        WRITE_TRACE_LOG_MB_TITLE(_rec_tst_enc, MbIndex, m_recCtx.MbWidth, m_recCtx.MbHeight, 0 == m_recCtx.m_pPicHeader->progressive_frame);
        WRITE_TRACE_LOG_MB_TITLE(_rec, MbIndex, m_recCtx.MbWidth, m_recCtx.MbHeight, 0 == m_recCtx.m_pPicHeader->progressive_frame);
        WRITE_TRACE_LOG_MB_TITLE(_vec_tst, MbIndex, m_recCtx.MbWidth, m_recCtx.MbHeight, 0 == m_recCtx.m_pPicHeader->progressive_frame);
        WRITE_TRACE_LOG_MB_TITLE(_vec_dir_tst, MbIndex, m_recCtx.MbWidth, m_recCtx.MbHeight, 0 == m_decCtx.m_pPicHeader->progressive_frame);
        WRITE_TRACE_LOG_MB_TITLE(_vec_final_tst, MbIndex, m_recCtx.MbWidth, m_recCtx.MbHeight, 0 == m_decCtx.m_pPicHeader->progressive_frame);


        // set the macro block info into the initial state
        // reset was done in EstimateBMacroBlocksRow
        //ResetMacroblock();

        // reset coefficient pointers
        m_pWriteCoeffs = (Ipp16s *)m_disCtx.m_pCoeffs;
        m_pReadCoeffs = m_encCtx.m_pCoeffs;

        // disassemble MB into parts
        //DisassembleBMacroBlock();
        
        // set Mb type
        //EncodePMacroBlockType();
        
        switch (m_pMbInfo->MbType)
        {
            case B_Skip:
                // encode MB into a bit stream
                EncodeSkipBMacroBlock();
                // reconstract B macroblock
                m_pReadCoeffs = m_encCtx.m_pCoeffs;
                ReconstructBMacroBlock();
                break;

            case I_8x8:
                // disassemble MB into parts
                DisassembleIMacroBlock();
                // put mb type into bitstream
                MbTypeIndex = CBPOfMacroblock2Code[AVS_INTRA][m_pMbInfo->MbCBP] + 24;
                //MbTypeIndex = (m_pMbInfo->MbType) + 5;
                PutUE(&(m_encCtx.m_stream),MbTypeIndex);
                WRITE_TRACE_LOG(_enc_tst, "mb type", MbTypeIndex);
                // encode MB into a bit stream
                EncodeIMacroBlock();
                // reconstract macroblock
                m_pReadCoeffs = m_encCtx.m_pCoeffs;
                ReconstructIMacroBlock();
                break;

            default:
                // disassemble MB into parts
                DisassembleBMacroBlock();
                // encode MB into a bit stream
                EncodeBMacroBlock();
                // reconstract B macroblock
                m_pReadCoeffs = m_encCtx.m_pCoeffs;
                ReconstructBMacroBlock();
                break;
        };

        MbIndex += 1;
        AdvanceNeighbours();
        AdvanceDisassembling();
        AdvanceEncoding();
        AdvanceReconstruction();

    } while (MbIndex < MbMax);

    // update context
    FinalizeDisassembling();
    FinalizeEncoding();
    FinalizeReconstruction();

} // void AVSCompressor::CompressBMacroBlocksRow(void)

void AVSCompressor::EncodeBMacroBlock(void)
{
    Ipp32u i;

    if (B_Direct_16x16 == m_pMbInfo->MbType)
    {
        //Encode Direct MB type
        if (m_encCtx.m_pPicHeader->skip_mode_flag)
        {
            PutUE(&m_encCtx.m_stream, m_pMbInfo->MbType - 2 - 5);
            WRITE_TRACE_LOG(_enc_tst, "mb type", m_pMbInfo->MbType - 2 - 5);
        }
        else
        {
            PutUE(&m_encCtx.m_stream, (m_pMbInfo->MbType) -1 - 5 );
            WRITE_TRACE_LOG(_enc_tst, "mb type", (m_pMbInfo->MbType) - 1 - 5);
        }
        GetRefIndiciesBSlice();
        GetDiffMotionVectorsBSlice();
    }
    else 
    {
        // decode block types
        if (B_8x8 == m_pMbInfo->MbType)
        {
            Ipp8u predType;
            for (i = 0; i < 4; i += 1)
            {
                predType = m_pMbInfo->predType[i];
                PutBits(&m_encCtx.m_stream,predType,2);
                WRITE_TRACE_LOG(_enc_tst, "mb part type", predType);

                //if (PredDirect != predType)
                //    m_pMbInfo->MvNum = (Ipp16u) (m_pMbInfo->MvNum + 1);
                // correct the 'symmetrical' block prediction type,
                // because our consts differ slightly from the standard's consts.
                //if (3 == predType)
                //    predType = PredSym;
                //
                //m_pMbInfo->predType[i] = (Ipp8u) predType;
            }
        }
        //
        if ((0 == m_encCtx.m_pPicHeader->picture_reference_flag) &&
            (0 == m_encCtx.m_pPicHeader->picture_structure))
        {
            //clone reference indices
            GetRefIndiciesBSlice();

            // Only field structure - indecies are 1 bit wide
            for (i = 0; i < m_pMbInfo->MvNum; i += 1)
            {
                PutBit(&m_encCtx.m_stream,m_refIdx[i]);
                WRITE_TRACE_LOG(_enc_tst, "mb reference index", m_refIdx[i]);
            }

        }

        //Encode MB type
        if (m_encCtx.m_pPicHeader->skip_mode_flag)
        {
            if (-1 == m_decCtx.SkipMbCount)
            {
                // put zero into bit stream (-1+1 = 0)
                PutUE(&m_encCtx.m_stream, m_encCtx.SkipMbCount + 1);
                WRITE_TRACE_LOG(_enc_tst, "skip mb count", m_encCtx.SkipMbCount + 1);
            }
            //all pervious Mb were skipped
            if ( 0 <= m_encCtx.SkipMbCount)
            {
                PutUE(&m_encCtx.m_stream, m_encCtx.SkipMbCount + 1);
                WRITE_TRACE_LOG(_enc_tst, "skip mb count", m_encCtx.SkipMbCount + 1);
                m_encCtx.SkipMbCount = -1;
            }
            PutUE(&m_encCtx.m_stream, m_pMbInfo->MbType - 2 - 5);
            WRITE_TRACE_LOG(_enc_tst, "mb type", m_pMbInfo->MbType - 2 - 5);
        }
        else
        {
            PutUE(&m_encCtx.m_stream, (m_pMbInfo->MbType) -1 - 5 );
            WRITE_TRACE_LOG(_enc_tst, "mb type", (m_pMbInfo->MbType) - 1 - 5);
        }
        
        GetRefIndiciesBSlice();
        GetDiffMotionVectorsBSlice();

        // encode motion vectors
        for (i = 0; i < m_pMbInfo->MvNum; i += 1)
        {
            PutSE(&m_encCtx.m_stream, mv_diff[i].vector.x);
            WRITE_TRACE_LOG(_enc_tst, "mv diff x", mv_diff[i].vector.x);
            PutSE(&m_encCtx.m_stream, mv_diff[i].vector.y);
            WRITE_TRACE_LOG(_enc_tst, "mv diff y", mv_diff[i].vector.y);
        }
    }
    
    // encode weighting prediction flag
    if (m_encCtx.m_pSlcHeader->mb_weighting_flag)
    {
        PutBit(&m_encCtx.m_stream, m_pMbInfo->weighting_prediction);
        WRITE_TRACE_LOG(_enc_tst, "weighting prediction", m_pMbInfo->weighting_prediction);
    }

    //For all types of pictures
    {
        Ipp32u uTemp;
        Ipp32s mb_qp_delta = 0;

        // encode coded block pattern
        uTemp = CBPOfMacroblock2Code[AVS_INTER][m_pMbInfo->MbCBP  & 0x3f];
        PutUE(&m_encCtx.m_stream, uTemp & 0x3f );
        WRITE_TRACE_LOG(_enc_tst, "CBP code", uTemp);
        
        // save additional chroma bits
        if (AVS_CHROMA_422_FORMAT == m_encCtx.m_pSeqHeader->chroma_format)
        {
            WRITE_TRACE_LOG(_enc_tst, "CBP code +", uTemp); 
            PutUE(&m_encCtx.m_stream, ((uTemp >> 6) & 0x3) );
        }

        // encode quantization parameter
        if ((m_pMbInfo->MbCBP) &&
            (0 == m_decCtx.FixedQP))
        {
            mb_qp_delta = m_pMbInfo->QP - m_encCtx.PreviousQP;
            PutSE(&m_encCtx.m_stream, mb_qp_delta);
            WRITE_TRACE_LOG(_enc_tst, "mb qp delta", mb_qp_delta);
            m_encCtx.PreviousQP = m_pMbInfo->QP;
        }
        //    
        {
        Ipp32u uBlockMask = 1;

        // encode luminance blocks
        for (i = 0; i < 4; i += 1, uBlockMask += uBlockMask)
        {
            if (m_pMbInfo->MbCBP & uBlockMask)
            {
                avs_EncodeLumaBlockInter_AVS_1u16s(&m_encCtx.m_stream.dst,
                                                   &m_encCtx.m_stream.offset,
                                                   m_pReadCoeffs,
                                                   m_encCtx.ScanType);
                m_pReadCoeffs += 64;
            }
        }

        // encode chrominance blocks
        for (; i < m_encCtx.iNumberOfBlocks; i += 1, uBlockMask += uBlockMask)
        {
            if (m_pMbInfo->MbCBP & uBlockMask)
            {
                avs_EncodeChromaBlock_AVS_1u16s(&m_encCtx.m_stream.dst,
                                                &m_encCtx.m_stream.offset,
                                                m_pReadCoeffs,
                                                m_encCtx.ScanType);
                m_pReadCoeffs += 64;
            }
        }
        }

    }

} // void AVSCompressor::EncodeBMacroBlock(void)

void AVSCompressor::EncodeSkipBMacroBlock(void)
{
    // skip Mb may be first Mb, so we should encode slice header
    // encode slice header
    
    // set QP
    m_pMbInfo->QP = m_encCtx.PreviousQP;

    //Encode MB type
    if ( 0 == m_decCtx.m_pPicHeader->skip_mode_flag)
    {
        PutUE(&m_encCtx.m_stream, (m_pMbInfo->MbType) - 1 - 5);
        WRITE_TRACE_LOG(_enc_tst, "mb type", (m_pMbInfo->MbType) -1 - 5);
        //Construct Motion Vectors for P Block
        //GetDiffMotionVectorsPSlice();
    }
    else
        m_encCtx.SkipMbCount++;
    
    //If all row consist only from skipped Mb
    //if ( m_encCtx.SkipMbCount == m_encCtx.MbWidth )
    //{
    //    PutUE(&m_encCtx.m_stream, m_decCtx.SkipMbCount + 1);
    //    WRITE_TRACE_LOG(_enc_tst, "skip mb count", m_decCtx.SkipMbCount + 1);
    //    m_encCtx.SkipMbCount = -1;
    //}
    //
    //if ((NULL == m_pMbInfoTop) ||
    //    ((0 == m_pMbInfoTop->refIdx[AVS_FORWARD][2]) &&
    //     (m_pMbInfoTop->predType[2] & PredForward) &&
    //     (0 == m_pMbInfoTop->mv[AVS_FORWARD][2].scalar)))
    //{
    //    m_mvPred.scalar = 0;
    //}
    //else if ((NULL == m_pMbInfoLeft) ||
    //         ((0 == m_pMbInfoLeft->refIdx[AVS_FORWARD][1]) &&
    //          (m_pMbInfoLeft->predType[1] & PredForward) &&
    //          (0 == m_pMbInfoLeft->mv[AVS_FORWARD][1].scalar)))
    //{
    //    m_mvPred.scalar = 0;
    //}
    //else
    {
        ReconstructMotionVectorsBSliceDirect();
    }

    WRITE_TRACE_LOG(_vec_final_tst, "FW motion vector X", m_pMbInfo->mv[AVS_FORWARD][0].vector.x);
    WRITE_TRACE_LOG(_vec_final_tst, "FW motion vector Y", m_pMbInfo->mv[AVS_FORWARD][0].vector.y);
    WRITE_TRACE_LOG(_vec_final_tst, "FW motion vector X", m_pMbInfo->mv[AVS_FORWARD][1].vector.x);
    WRITE_TRACE_LOG(_vec_final_tst, "FW motion vector Y", m_pMbInfo->mv[AVS_FORWARD][1].vector.y);
    WRITE_TRACE_LOG(_vec_final_tst, "FW motion vector X", m_pMbInfo->mv[AVS_FORWARD][2].vector.x);
    WRITE_TRACE_LOG(_vec_final_tst, "FW motion vector Y", m_pMbInfo->mv[AVS_FORWARD][2].vector.y);
    WRITE_TRACE_LOG(_vec_final_tst, "FW motion vector X", m_pMbInfo->mv[AVS_FORWARD][3].vector.x);
    WRITE_TRACE_LOG(_vec_final_tst, "FW motion vector Y", m_pMbInfo->mv[AVS_FORWARD][3].vector.y);
    WRITE_TRACE_LOG(_vec_final_tst, "BW motion vector X", m_pMbInfo->mv[AVS_BACKWARD][0].vector.x);
    WRITE_TRACE_LOG(_vec_final_tst, "BW motion vector Y", m_pMbInfo->mv[AVS_BACKWARD][0].vector.y);
    WRITE_TRACE_LOG(_vec_final_tst, "BW motion vector X", m_pMbInfo->mv[AVS_BACKWARD][1].vector.x);
    WRITE_TRACE_LOG(_vec_final_tst, "BW motion vector Y", m_pMbInfo->mv[AVS_BACKWARD][1].vector.y);
    WRITE_TRACE_LOG(_vec_final_tst, "BW motion vector X", m_pMbInfo->mv[AVS_BACKWARD][2].vector.x);
    WRITE_TRACE_LOG(_vec_final_tst, "BW motion vector Y", m_pMbInfo->mv[AVS_BACKWARD][2].vector.y);
    WRITE_TRACE_LOG(_vec_final_tst, "BW motion vector X", m_pMbInfo->mv[AVS_BACKWARD][3].vector.x);
    WRITE_TRACE_LOG(_vec_final_tst, "BW motion vector Y", m_pMbInfo->mv[AVS_BACKWARD][3].vector.y);
    
} // void EncodeSkipBMacroBlock(void)

Ipp32u AVSCompressor::GetSizeBMacroBlock(void)
{
    Ipp32u requiredSize = 0;

    // encode coded block pattern
    {
        Ipp32u uTemp;

        // convert CBP to code
        uTemp = CBPOfMacroblock2Code[AVS_INTER][m_pMbInfo->MbCBP  & 0x3f];

        // save it to the bit stream
        requiredSize += GetUESize(uTemp);
        // save additional chroma bits
        if (AVS_CHROMA_422_FORMAT == m_encCtx.m_pSeqHeader->chroma_format)
        {
            uTemp = (m_pMbInfo->MbCBP >> 6) & 0x3;

            requiredSize += GetUESize(uTemp);
        }
    }

    // encode quantization parameter
    if ((m_pMbInfo->MbCBP) &&
        (0 == m_encCtx.FixedQP))
    {
        Ipp32s mb_qp_delta;

        // get QP delta
        mb_qp_delta = m_encCtx.PreviousQP - m_pMbInfo->QP;

        // save delta to the bit stream
        requiredSize += GetSESize(mb_qp_delta);
    }

    // encode blocks coefficients
    {
        Ipp32u i;
        Ipp32u uBlockMask = 1;
        const Ipp16s *pReadCoeffs = m_pReadCoeffs;

        // decode luminance blocks
        for (i = 0; i < 4; i += 1, uBlockMask += uBlockMask)
        {
            if (m_pMbInfo->MbCBP & uBlockMask)
            {
                requiredSize += avs_GetSizeLumaBlockInter_AVS_1u16s(pReadCoeffs,
                                                                    m_encCtx.ScanType);
                pReadCoeffs += 64;
            }
        }

        // encode chrominance blocks
        for (; i < m_encCtx.iNumberOfBlocks; i += 1, uBlockMask += uBlockMask)
        {
            if (m_pMbInfo->MbCBP & uBlockMask)
            {
                requiredSize += avs_GetSizeChromaBlock_AVS_1u16s(pReadCoeffs,
                                                                 m_encCtx.ScanType);
                pReadCoeffs += 64;
            }
        }
    }

    return requiredSize;

} // Ipp32u AVSCompressor::GetSizeBMacroBlock(void)


void AVSCompressor::GetRefIndiciesBSlice(void)
{
    eAVSPredType predType;
    Ipp32u dir;
    Ipp32s iRefNum = 0;

    // cycle on directions
    predType = PredForward;
    for (dir = AVS_FORWARD; dir <= AVS_BACKWARD; dir += 1)
    {
        // clone reference indecies
        switch (m_pMbInfo->divType)
        {
            // all block have the same reference index
        case Div_16x16:
            if (m_pMbInfo->predType[0] & predType)
            {
                m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][0];
                iRefNum += 1;
            }
            break;

            // top blocks have index 0,
            // bottom blocks have index 1
        case Div_16x8:
            if (m_pMbInfo->predType[0] & predType)
            {
                m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][0];                
                iRefNum += 1;
            }
            if (m_pMbInfo->predType[2] & predType)
            {
                m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][2];
                iRefNum += 1;
            }
            break;

            // left blocks have index 0,
            // right blocks have index 1
        case Div_8x16:
            if (m_pMbInfo->predType[0] & predType)
            {
                m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][0];
                iRefNum += 1;
            }
            if (m_pMbInfo->predType[1] & predType)
            {
                m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][1];
                iRefNum += 1;
            }
            break;

            // every block has its own reference index
        default:
            if (m_pMbInfo->predType[0] & predType)
            {
                m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][0];
                iRefNum += 1;
            }
            if (m_pMbInfo->predType[1] & predType)
            {
                m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][1];
                iRefNum += 1;
            }
            if (m_pMbInfo->predType[2] & predType)
            {
                m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][2];
                iRefNum += 1;
            }
            if (m_pMbInfo->predType[3] & predType)
            {
                m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][30];
                iRefNum += 1;
            }
            break;
        }

        // set other direction
        predType = PredBackward;
    }

}// void AVSCompressor::GetRefIndiciesBSlice(void)

void AVSCompressor::GetDiffMotionVectorsBSlice(void)
{
    switch (m_pMbInfo->divType)
    {
    case Div_16x16:
        GetDiffMotionVectorsBSlice16x16();
        break;

    case Div_16x8:
        GetDiffMotionVectorsBSlice16x8();
        break;

    case Div_8x16:
        GetDiffMotionVectorsBSlice8x16();
        break;

    default:
        GetDiffMotionVectorsBSlice8x8();
        break;
    }

    WRITE_TRACE_LOG(_vec_final_tst, "FW motion vector X", m_pMbInfo->mv[AVS_FORWARD][0].vector.x);
    WRITE_TRACE_LOG(_vec_final_tst, "FW motion vector Y", m_pMbInfo->mv[AVS_FORWARD][0].vector.y);
    WRITE_TRACE_LOG(_vec_final_tst, "FW motion vector X", m_pMbInfo->mv[AVS_FORWARD][1].vector.x);
    WRITE_TRACE_LOG(_vec_final_tst, "FW motion vector Y", m_pMbInfo->mv[AVS_FORWARD][1].vector.y);
    WRITE_TRACE_LOG(_vec_final_tst, "FW motion vector X", m_pMbInfo->mv[AVS_FORWARD][2].vector.x);
    WRITE_TRACE_LOG(_vec_final_tst, "FW motion vector Y", m_pMbInfo->mv[AVS_FORWARD][2].vector.y);
    WRITE_TRACE_LOG(_vec_final_tst, "FW motion vector X", m_pMbInfo->mv[AVS_FORWARD][3].vector.x);
    WRITE_TRACE_LOG(_vec_final_tst, "FW motion vector Y", m_pMbInfo->mv[AVS_FORWARD][3].vector.y);
    WRITE_TRACE_LOG(_vec_final_tst, "BW motion vector X", m_pMbInfo->mv[AVS_BACKWARD][0].vector.x);
    WRITE_TRACE_LOG(_vec_final_tst, "BW motion vector Y", m_pMbInfo->mv[AVS_BACKWARD][0].vector.y);
    WRITE_TRACE_LOG(_vec_final_tst, "BW motion vector X", m_pMbInfo->mv[AVS_BACKWARD][1].vector.x);
    WRITE_TRACE_LOG(_vec_final_tst, "BW motion vector Y", m_pMbInfo->mv[AVS_BACKWARD][1].vector.y);
    WRITE_TRACE_LOG(_vec_final_tst, "BW motion vector X", m_pMbInfo->mv[AVS_BACKWARD][2].vector.x);
    WRITE_TRACE_LOG(_vec_final_tst, "BW motion vector Y", m_pMbInfo->mv[AVS_BACKWARD][2].vector.y);
    WRITE_TRACE_LOG(_vec_final_tst, "BW motion vector X", m_pMbInfo->mv[AVS_BACKWARD][3].vector.x);
    WRITE_TRACE_LOG(_vec_final_tst, "BW motion vector Y", m_pMbInfo->mv[AVS_BACKWARD][3].vector.y);

} // void AVSCompressor::GetDiffMotionVectorsBSlice(void)

void AVSCompressor::GetDiffMotionVectorsBSlice16x16(void)
{
        Ipp8u *pPredType = m_pMbInfo->predType;

    // get the forward motion vector prediction & reconstruct the motion vector
    if (pPredType[0] & PredForward)
    {
        GetMotionVectorPredictor16x16(PredForward, 0);
        mv_diff[0] = m_pMbInfo->mv[AVS_FORWARD][0] - m_mvPred;
    }

    if (PredForward != pPredType[0])
    {
        // reconstruct the backward motion vector
        if (PredBackward == pPredType[0])
        {
            GetMotionVectorPredictor16x16(PredBackward, 0);
            mv_diff[0] = m_pMbInfo->mv[AVS_BACKWARD][0] - m_mvPred;
        }
        // create the symmetrical backward motion vector
        else
        {
            //CreateSymmetricalMotionVector(0);
            mv_diff[0] = m_pMbInfo->mv[AVS_FORWARD][0] - m_mvPred;
        }
        //mv_diff[0] = m_pMbInfo->mv[AVS_BACKWARD][0] - m_mvPred;
        //mv_diff[0] = m_pMbInfo->mv[AVS_FORWARD][0] - m_mvPred;
    }

} //void AVSCompressor::GetDiffMotionVectorsBSlice16x16(void)
#undef AVS_PREPARE_NEIGHBOURS_16x8_0
#define AVS_PREPARE_NEIGHBOURS_16x8_0 \
    m_neighbours.pNearest = m_pMbInfoTop; \
    m_neighbours.pTop = m_pMbInfoTop; \
    m_neighbours.pTopRight = m_pMbInfoTopRight; \
    m_neighbours.pTopLeft = m_pMbInfoTopLeft;

#undef AVS_PREPARE_NEIGHBOURS_16x8_1
#define AVS_PREPARE_NEIGHBOURS_16x8_1 \
    m_neighbours.pNearest = m_pMbInfoLeft; \
    m_neighbours.pTop = m_pMbInfo; \
    m_neighbours.pTopRight = NULL; \
    m_neighbours.pTopLeft = m_pMbInfoLeft;

void AVSCompressor::GetDiffMotionVectorsBSlice16x8(void)
{
    Ipp8u *pPredType = m_pMbInfo->predType;
    Ipp32s numMV;

    numMV = 0;
    // get the motion vector prediction & construct motion vector
    if (pPredType[0] & PredForward)
    {
        AVS_PREPARE_NEIGHBOURS_16x8_0
        GetMotionVectorPredictor16x8(PredForward, 0);
        mv_diff[numMV++] = m_pMbInfo->mv[AVS_FORWARD][0] - m_mvPred;

    }

    // get the motion vector prediction & construct motion vector
    if (pPredType[2] & PredForward)
    {
        AVS_PREPARE_NEIGHBOURS_16x8_1
        GetMotionVectorPredictor16x8(PredForward, 2);
        mv_diff[numMV++] = m_pMbInfo->mv[AVS_FORWARD][2] - m_mvPred;
    }

    if (PredForward != pPredType[0])
    {
        // reconstruct the backward motion vector
        if (PredBackward == pPredType[0])
        {
            AVS_PREPARE_NEIGHBOURS_16x8_0
            GetMotionVectorPredictor16x8(PredBackward, 0);
            mv_diff[numMV++] = m_pMbInfo->mv[AVS_BACKWARD][0] - m_mvPred;
        }
        // create the symmetrical backward motion vector
        else
        {
            CreateSymmetricalMotionVector(0);
            //mvTemp = m_mvPred;
            mv_diff[numMV++].vector.x = 0;
            mv_diff[numMV++].vector.y = 0;

            // correct prediction type
            pPredType[0] = PredBiDir;
            pPredType[1] = PredBiDir;
        }
    }

    if (PredForward != pPredType[2])
    {

        // reconstruct the backward motion vector
        if (PredBackward == pPredType[2])
        {
            AVS_PREPARE_NEIGHBOURS_16x8_1
            GetMotionVectorPredictor16x8(PredBackward, 2);
            mv_diff[numMV++] = m_pMbInfo->mv[AVS_BACKWARD][2] - m_mvPred;
        }
        // create the symmetrical backward motion vector
        else
        {
            CreateSymmetricalMotionVector(2);
            //mvTemp = m_mvPred;
            mv_diff[numMV++].vector.x = 0;
            mv_diff[numMV++].vector.y = 0;

            // correct prediction type
            pPredType[2] = PredBiDir;
            pPredType[3] = PredBiDir;
        }

    }    
} //void AVSCompressor::GetDiffMotionVectorsBSlice16x8(void)

#undef AVS_PREPARE_NEIGHBOURS_8x16_0
#define AVS_PREPARE_NEIGHBOURS_8x16_0 \
    m_neighbours.pNearest = m_pMbInfoLeft; \
    m_neighbours.pLeft = m_pMbInfoLeft; \
    m_neighbours.pTopRight = m_pMbInfoTop; \
    m_neighbours.pTopLeft = NULL; \

#undef AVS_PREPARE_NEIGHBOURS_8x16_1
#define AVS_PREPARE_NEIGHBOURS_8x16_1 \
    m_neighbours.pNearest = (m_pMbInfoTopRight) ? (m_pMbInfoTopRight) : (m_pMbInfoTop); \
    m_neighbours.pLeft = m_pMbInfo; \
    m_neighbours.pTopRight = m_pMbInfoTopRight; \
    m_neighbours.pTopLeft = m_pMbInfoTop;

void AVSCompressor::GetDiffMotionVectorsBSlice8x16(void)
{
    Ipp8u *pPredType = m_pMbInfo->predType;
    Ipp32s numMV;

    numMV = 0;
    // get the motion vector prediction & reconstruct motion vector
    if (pPredType[0] & PredForward)
    {
        AVS_PREPARE_NEIGHBOURS_8x16_0
        GetMotionVectorPredictor8x16(PredForward, 0);
        mv_diff[numMV++] = m_pMbInfo->mv[AVS_FORWARD][0] - m_mvPred;
    }

    // get the motion vector prediction & reconstruct motion vector
    if (pPredType[1] & PredForward)
    {
        AVS_PREPARE_NEIGHBOURS_8x16_1
        GetMotionVectorPredictor8x16(PredForward, 1);
        mv_diff[numMV++] = m_pMbInfo->mv[AVS_FORWARD][1] - m_mvPred;
    }

    if (PredForward != pPredType[0])
    {
        // reconstruct the backward motion vector
        if (PredBackward == pPredType[0])
        {
            AVS_PREPARE_NEIGHBOURS_8x16_0
            GetMotionVectorPredictor8x16(PredBackward, 0);
            mv_diff[numMV++] = m_pMbInfo->mv[AVS_BACKWARD][0] - m_mvPred;
        }
        // create the symmetrical backward motion vector
        else
        {
            CreateSymmetricalMotionVector(0);
            //mvTemp = m_mvPred;
            mv_diff[numMV++].vector.x = 0;
            mv_diff[numMV++].vector.y = 0;

            // correct prediction type
            pPredType[0] = PredBiDir;
            pPredType[2] = PredBiDir;
        }
    }

    if (PredForward != pPredType[1])
    {
        AVSMVector mvTemp;

        // reconstruct the backward motion vector
        if (PredBackward == pPredType[1])
        {
            AVS_PREPARE_NEIGHBOURS_8x16_1
            GetMotionVectorPredictor8x16(PredBackward, 1);
            mvTemp = mv_diff[numMV++] = m_pMbInfo->mv[AVS_BACKWARD][1] - m_mvPred;
        }
        // create the symmetrical backward motion vector
        else
        {
            CreateSymmetricalMotionVector(1);
            //mvTemp = m_mvPred;
            mv_diff[numMV++].vector.x = 0;
            mv_diff[numMV++].vector.y = 0;

            // correct prediction type
            pPredType[1] = PredBiDir;
            pPredType[3] = PredBiDir;
        }
    }

} //void AVSCompressor::GetDiffMotionVectorsBSlice8x16(void)

#undef AVS_PREPARE_NEIGHBOURS_8x8_0
#define AVS_PREPARE_NEIGHBOURS_8x8_0 \
    m_neighbours.pLeft = m_pMbInfoLeft; \
    m_neighbours.pTop = m_pMbInfoTop; \
    m_neighbours.pTopRight = m_pMbInfoTop; \
    m_neighbours.pTopLeft = NULL;

#undef AVS_PREPARE_NEIGHBOURS_8x8_1
#define AVS_PREPARE_NEIGHBOURS_8x8_1 \
    m_neighbours.pLeft = m_pMbInfo; \
    m_neighbours.pTop = m_pMbInfoTop; \
    m_neighbours.pTopRight = m_pMbInfoTopRight; \
    m_neighbours.pTopLeft = m_pMbInfoTop;

#undef AVS_PREPARE_NEIGHBOURS_8x8_2
#define AVS_PREPARE_NEIGHBOURS_8x8_2 \
    m_neighbours.pLeft = m_pMbInfoLeft; \
    m_neighbours.pTop = m_pMbInfo; \
    m_neighbours.pTopRight = m_pMbInfo; \
    m_neighbours.pTopLeft = NULL;

#undef AVS_PREPARE_NEIGHBOURS_8x8_3
#define AVS_PREPARE_NEIGHBOURS_8x8_3 \
    m_neighbours.pLeft = m_pMbInfo; \
    m_neighbours.pTop = m_pMbInfo; \
    m_neighbours.pTopRight = NULL; \
    m_neighbours.pTopLeft = m_pMbInfo;

void AVSCompressor::GetDiffMotionVectorsBSlice8x8(void)
{
    Ipp8u *pPredType = m_pMbInfo->predType;
    Ipp32s numMV;

    numMV = 0;

    if (pPredType[0] & PredForward)
    {
        AVS_PREPARE_NEIGHBOURS_8x8_0
        GetMotionVectorPredictor8x8(PredForward, 0);
        mv_diff[numMV++] = m_pMbInfo->mv[AVS_FORWARD][0] - m_mvPred;
    }

    if (pPredType[1] & PredForward)
    {
        AVS_PREPARE_NEIGHBOURS_8x8_1
        GetMotionVectorPredictor8x8(PredForward, 1);
        mv_diff[numMV++] = m_pMbInfo->mv[AVS_FORWARD][1] - m_mvPred;
    }

    if (pPredType[2] & PredForward)
    {
        AVS_PREPARE_NEIGHBOURS_8x8_2
        GetMotionVectorPredictor8x8(PredForward, 2);
        mv_diff[numMV++] = m_pMbInfo->mv[AVS_FORWARD][2] - m_mvPred;
    }

    if (pPredType[3] & PredForward)
    {
        AVS_PREPARE_NEIGHBOURS_8x8_3
        GetMotionVectorPredictor8x8(PredForward, 3);
        mv_diff[numMV++] = m_pMbInfo->mv[AVS_FORWARD][3] - m_mvPred;
    }

    {
        // construct the backward motion vector
        if (PredBackward == pPredType[0])
        {
            AVS_PREPARE_NEIGHBOURS_8x8_0
            GetMotionVectorPredictor8x8(PredBackward, 0);
            mv_diff[numMV++] = m_pMbInfo->mv[AVS_FORWARD][0] - m_mvPred;
        }
        // create the symmetrical backward motion vector
        else if (PredSym == pPredType[0])
        {
            CreateSymmetricalMotionVector(0);
            //m_pMbInfo->mv[AVS_BACKWARD][0] = m_mvPred;
            mv_diff[numMV++].vector.x = 0;
            mv_diff[numMV++].vector.y = 0;

            // correct prediction type
            pPredType[0] = PredBiDir;
        }
    }

    {
        // reconstruct the backward motion vector
        if (PredBackward == pPredType[1])
        {
            AVS_PREPARE_NEIGHBOURS_8x8_1
            GetMotionVectorPredictor8x8(PredBackward, 1);
            mv_diff[numMV++] = m_pMbInfo->mv[AVS_FORWARD][1] - m_mvPred;
        }
        // create the symmetrical backward motion vector
        else if (PredSym == pPredType[1])
        {
            CreateSymmetricalMotionVector(1);
            //m_pMbInfo->mv[AVS_BACKWARD][1] = m_mvPred;
            mv_diff[numMV++].vector.x = 0;
            mv_diff[numMV++].vector.y = 0;

            // correct prediction type
            pPredType[1] = PredBiDir;
        }
    }

    {
        // reconstruct the backward motion vector
        if (PredBackward == pPredType[2])
        {
            AVS_PREPARE_NEIGHBOURS_8x8_2
            GetMotionVectorPredictor8x8(PredBackward, 2);
            mv_diff[numMV++] = m_pMbInfo->mv[AVS_FORWARD][2] - m_mvPred;
        }
        // create the symmetrical backward motion vector
        else if (PredSym == pPredType[2])
        {
            CreateSymmetricalMotionVector(2);
            //m_pMbInfo->mv[AVS_BACKWARD][2] = m_mvPred;
            mv_diff[numMV++].vector.x = 0;
            mv_diff[numMV++].vector.y = 0;

            // correct prediction type
            pPredType[2] = PredBiDir;
        }
    }

    {
        // reconstruct the backward motion vector
        if (PredBackward == pPredType[3])
        {
            AVS_PREPARE_NEIGHBOURS_8x8_3
            GetMotionVectorPredictor8x8(PredBackward, 3);
            mv_diff[numMV++] = m_pMbInfo->mv[AVS_FORWARD][3] - m_mvPred;
        }
        // create the symmetrical backward motion vector
        else if (PredSym == pPredType[3])
        {
            CreateSymmetricalMotionVector(3);
            //m_pMbInfo->mv[AVS_BACKWARD][3] = m_mvPred;
            mv_diff[numMV++].vector.x = 0;
            mv_diff[numMV++].vector.y = 0;


            // correct prediction type
            pPredType[3] = PredBiDir;
        }
    }


} //void AVSCompressor::GetDiffMotionVectorsBSlice8x8(void)

//void AVSCompressor::ReconstructBMacroBlock(void)
//{
//    Ipp32u chromaQP;
//
//    WRITE_TRACE_LOG(_rec_tst_enc, "CurrentQP", m_pMbInfo->QP);
//
//    // compensate luminance motion
//    AVSDecompressor::CompensateMotionLumaBMacroBlock();
//
//    // reconstruct luminance blocks
//    if (m_pMbInfo->MbCBP & 0x0f)
//    {
//        ippiReconstructLumaInter_AVS_16s8u_C1R((Ipp16s **) &m_pReadCoeffs,
//                                               m_recCtx.m_pPlanes8u[0],
//                                               m_recCtx.m_iPitch,
//                                               m_pMbInfo->NumCoeffs,
//                                               m_pMbInfo->MbCBP,
//                                               m_pMbInfo->QP);
//    }
//
//    WRITE_TRACE_LOG_BYTE_BLOCK(_rec_tst_enc, "Y block", m_recCtx.m_pPlanes8u[0], m_recCtx.m_iPitch, 16, 16);
//
//    chromaQP = AVSChromaQPTable[m_pMbInfo->QP];
//    WRITE_TRACE_LOG(_rec_tst_enc, "chromaQP", chromaQP);
//
//    // compensate chrominance motion
//    AVSDecompressor::CompensateMotionChromaBMacroBlock();
//
//    // reconstruct chrominance blocks
//    if (m_pMbInfo->MbCBP & 0x0f0)
//    {
//        ippiReconstructChromaInter_AVS_16s8u_C1R((Ipp16s **) &m_pReadCoeffs,
//                                                 m_recCtx.m_pPlanes8u[1],
//                                                 m_recCtx.m_pPlanes8u[2],
//                                                 m_recCtx.m_iPitch,
//                                                 m_pMbInfo->NumCoeffs + 4,
//                                                 m_pMbInfo->MbCBP >> 4,
//                                                 chromaQP);
//    }
//
//    WRITE_TRACE_LOG_BYTE_BLOCK(_rec_tst_enc, "U block", m_recCtx.m_pPlanes8u[1], m_recCtx.m_iPitch, 8, 8);
//    WRITE_TRACE_LOG_BYTE_BLOCK(_rec_tst_enc, "V block", m_recCtx.m_pPlanes8u[2], m_recCtx.m_iPitch, 8, 8);
//
//} // void AVSCompressor::ReconstructBMacroBlock(void)

} // namespace UMC

#endif // defined(UMC_ENABLE_AVS_VIDEO_ENCODER)
